<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Film } from '@vben/icons'
import { $t } from '@vben/locales'
import { obsUpload, obsDelete } from '#/utils/obs'
import { getStemImgsBlob } from "#/utils/getImgUrl"

defineOptions({ name: 'UploadFile' })

interface FileData {
  url?: string
  path?: string
  file?: File
}

const props = defineProps<{
  modelValue?: string
  accept?: string
  maxSize?: number // 单位：MB
  showDelete?: boolean
  disabled?: boolean
  uploadTips?: string
  limit?: number
  showFileList?: boolean
  isVideo?: boolean
  listType?: 'text' | 'picture' | 'picture-card'
}>()

const emit = defineEmits<{
  'update:modelValue': [value: string]
  'upload-success': [url: string, path: string]
  'delete-success': []
}>()

// 文件数据
const fileData = ref<FileData | undefined>(undefined)
// 上传状态
const uploading = ref(false)
// 默认配置
const defaultAccept = '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
const defaultMaxSize = 5 // 5MB

// 计算属性
const acceptTypes = computed(() => props.accept || defaultAccept)
const maxSizeBytes = computed(() => (props.maxSize || defaultMaxSize) * 1024 * 1024)

// 监听外部数据变化
const initFileData = async (val: string | FileData | undefined) => {
  if (val) {
    const path = typeof val === 'string' ? val : (val as FileData).path
    if (path) {
      if (!path.startsWith('blob:')) {
        // 如果是 OBS 路径，需要从云上获取 blob
        const blob = await getStemImgsBlob(path)
        if (blob instanceof Blob) {
          fileData.value = {
            url: URL.createObjectURL(blob),
            path: path,
            file: blob as File
          }
        }
      }
    }
  } else {
    // 当传入 undefined 或空值时，清空文件数据
    fileData.value = undefined
  }
}

// 监听 props.modelValue 变化
watch(() => props.modelValue, (newVal) => {
  initFileData(newVal)
}, { immediate: true })

// 处理文件上传
const handleUpload = (options: any) => {
  const file = options.file
  // 验证文件大小
  if (file.size > maxSizeBytes.value) {
    ElMessage.error($t('common.fileSizeLimitTxt', [props.maxSize || defaultMaxSize.toString()]))
    return
  }

  // 创建本地预览URL
  const localUrl = URL.createObjectURL(file)
  const newFileData: FileData = {
    url: localUrl,
    file: file
  }
  fileData.value = newFileData
  emit('update:modelValue', newFileData.url || '')
}

// 上传文件到OBS
const uploadToObs = async (): Promise<{ path: string } | null> => {
  if (!fileData.value?.file) {
    return null
  }
  uploading.value = true
  if (!fileData.value!.file!.name) return null;
  try {
    return new Promise((resolve, reject) => {
      obsUpload({
        Key: fileData.value!.file!.name,
        SourceFile: fileData.value!.file!,
        ContentType: fileData.value!.file!.type
      }, (err: any, result: any, uploadedKey: string) => {
        if (err) {
          return
        }
        emit('update:modelValue', uploadedKey)
        // 释放本地预览URL
        if (fileData.value?.url && fileData.value.url.startsWith('blob:')) {
          URL.revokeObjectURL(fileData.value.url)
        }
        resolve({ path: uploadedKey })
      })
    })
  } catch (error) {
    return null
  } finally {
    uploading.value = false
  }
}

// 删除文件
const handleDeleteFile = async () => {
  if (!fileData.value) return
  try {
    // 如果文件已上传到OBS，需要删除
    if (fileData.value.path && !fileData.value.path.startsWith('blob:')) {
      await new Promise((resolve, reject) => {
        obsDelete({
          Key: fileData.value!.path!
        }, (err: any) => {
          if (err) {
            ElMessage.error($t('common.deleteFailed'))
            reject(err)
          } else {
            resolve(true)
          }
        })
      })
    }
    // 释放本地预览URL
    if (fileData.value.url && fileData.value.url.startsWith('blob:')) {
      URL.revokeObjectURL(fileData.value.url)
    }
    // 清空文件数据
    fileData.value = undefined
    emit('update:modelValue', '')
    ElMessage.success($t('common.deleteSuccess'))
  } catch (error) {
  }
}

// 处理文件超出限制
const handleExceed = () => {
  ElMessage.error($t('common.fileLimitTxt', [props.limit || 1]))
}

// 处理文件删除
const handleRemove = () => {
  fileData.value = undefined
  emit('update:modelValue', '')
}

// 暴露上传方法
defineExpose({
  uploadToObs,
  handleDeleteFile,
  initFileData
})
</script>

<template>
  <div class="file-upload-container">
    <video width="360px" height="200px" v-if="props.isVideo && fileData?.file" :src="fileData?.url || ''" controls></video>
    <el-upload :controls="false" :on-remove="handleRemove" :class="props.isVideo && fileData?.file ? 'upload-video-full' : null" action="#"
      :list-type="props.listType || 'text'" :show-file-list="props.showFileList" :accept="acceptTypes"
      :limit="props.limit" :http-request="handleUpload" :on-exceed="handleExceed" :auto-upload="true"
      :disabled="disabled">
      <el-button v-if="!props.isVideo" :icon="Plus" size="small">{{ $t('common.uploadFileTxt') }}</el-button>
      <div v-if="props.isVideo"
        class="flex items-center justify-center w-[360px] h-[200px] border-[1px] border-[#DCDFE6] rounded-[4px]">
        <Film />
      </div>
      <template v-if="!props.isVideo" #tip>
        <div class="inline-block text-xs text-gray-400 ml-[20px]">
          {{ props.uploadTips }}
        </div>
      </template>
    </el-upload>

  </div>
</template>

<style scoped>
/* 移除 Element Plus 上传组件的默认样式 */
:deep(.el-upload.el-upload--text) {
  display: inline-block;
}

:deep(.el-upload .el-upload-dragger) {
  width: 100% !important;
  height: 100% !important;
  border: none !important;
  background: transparent !important;
  border-radius: 4px !important;
}

.upload-video-full {
  :deep(.el-upload.el-upload--text) {
    display: none;
  }
}
</style>
